/*******************************************************************************
 * Copyright (c) 2002 Boris Pruessmann and others. All rights reserved. This
 * program and the accompanying materials are made available under the terms of
 * the Common Public License v0.5 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors: Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.ui.prefs;

import com.google.common.collect.Lists;

import net.sourceforge.perforce.ui.internal.Policy;

import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.preference.StringFieldEditor;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.dialogs.ListSelectionDialog;

import java.util.List;
import java.util.Map;

/**
 * @version $Revision: 488 $
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
class FormatFieldEditor extends StringFieldEditor {
  // ---------------------------------------------------------------------------
  // Private Variables

  /** Stores the reference to the FieldEditor's button control. */
  private Button buttonControl;
  /** Stores the text for the button. */
  private String buttonText;
  /** Stores the bindings for the add variables dialog. */
  private Map<String, String> bindings;

  // -------------------------------------------------------------------------
  // Public Constructors

  /**
   * Constructor for FormatFieldEditor.
   * 
   * @param name the name of the preference.
   * @param labelText the label text.
   * @param buttonText the button text.
   * @param newBindings the bindings.
   * @param parent the parent composite.
   */
  public FormatFieldEditor(String name, String labelText, String buttonText,
      Map<String, String> newBindings, Composite parent) {
    super(name, labelText + "/" + buttonText, parent); //$NON-NLS-1$
    bindings = newBindings;
  }

  //----------------------------------------------------------------------------
  // -- Public Methods

  @Override
  public int getNumberOfControls() {
    return super.getNumberOfControls() + 1;
  }

  @Override
  protected void createControl(Composite parent) {
    GridLayout layout = (GridLayout) parent.getLayout();
    doFillIntoGrid(parent, layout.numColumns);
  }

  /**
   * Returns the FielEditor's button control.
   * <p>
   * The control is created if it does not yet exist
   * </p>
   * 
   * @param parent the parent
   * @return the text control
   */
  public Button getButtonControl(Composite parent) {
    if (buttonControl == null) {
      buttonControl = new Button(parent, SWT.NONE);
      buttonControl.setText(buttonText);
      buttonControl.setFont(parent.getFont());

      GridData data = new GridData();
      data.horizontalAlignment = GridData.FILL;
      data.heightHint = convertVerticalDLUsToPixels(buttonControl, IDialogConstants.BUTTON_HEIGHT);
      int widthHint = convertHorizontalDLUsToPixels(buttonControl, IDialogConstants.BUTTON_WIDTH);
      data.widthHint =
          Math.max(widthHint, buttonControl.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
      buttonControl.setLayoutData(data);
      buttonControl.addListener(SWT.Selection, new Listener() {
        public void handleEvent(Event event) {
          addVariables();
        }
      });
    } else {
      checkParent(buttonControl, parent);
    }

    return buttonControl;
  }

  // ---------------------------------------------------------------------------
  // Protected Methods

  @Override
  protected void init(String name, String labelText) {
    int pos = labelText.indexOf('/');
    super.init(name, labelText.substring(0, pos));
    buttonText = labelText.substring(pos + 1);
  }

  @Override
  protected void adjustForNumColumns(int numColumns) {
    super.adjustForNumColumns(numColumns - 1);
  }


  @Override
  protected void doFillIntoGrid(Composite parent, int numColumns) {
    super.doFillIntoGrid(parent, numColumns - 1);
    getButtonControl(parent);
  }

  // ---------------------------------------------------------------------------
  // Private Variables

  /**
   * Displays the add variables dialog and adds the selected variables to the
   * format control,
   */
  private void addVariables() {
    ILabelProvider labelProvider = new LabelProvider() {
      @Override
      public String getText(Object element) {
        StringPair stringPair = (StringPair) element;
        return stringPair.getName() + " - " + stringPair.getDescription(); //$NON-NLS-1$
      }
    };

    final StringPair[] variables = getStringPairs();
    IStructuredContentProvider contentsProvider = new IStructuredContentProvider() {
      public Object[] getElements(Object inputElement) {
        return variables;
      }

      public void dispose() {
      }

      public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
      }
    };

    Shell shell = getTextControl().getShell();
    String message = Policy.bind("FormatFieldEditor." + //$NON-NLS-1$
        "Select_the_&variables_to_add_to_the_decoration_format"); //$NON-NLS-1$
    ListSelectionDialog dialog =
        new ListSelectionDialog(shell, this, contentsProvider, labelProvider, message);

    dialog.setTitle(Policy.bind("FormatFieldEditor.Add_Variables")); //$NON-NLS-1$
    if (dialog.open() != Dialog.OK) {
      return;
    }

    Object[] result = dialog.getResult();
    for (int i = 0; i < result.length; i++) {
      getTextControl().insert('{' + ((StringPair) result[i]).getName() + '}');
    }
  }

  /**
   * Create a StringPair array of the editor's bindings.
   * 
   * @return the StringPair array.
   */
  private StringPair[] getStringPairs() {
    final List<StringPair> result = Lists.newArrayListWithExpectedSize(bindings.size());
    for (String first : bindings.keySet()) {
      StringPair variable = new StringPair(first, bindings.get(first));
      result.add(variable);
    }

    return result.toArray(new StringPair[result.size()]);
  }

  // ----------------------------------------------------------------------
  // Private Nested Classes

  /** Maintains the relationship between a binding and its description. */
  private static class StringPair {
    // -----------------------------------------------------------------------
    // Private Variables

    /** Stores the binding */
    private String name;
    /** Stores the description. */
    private String description;

    // ---------------------------------------------------------------------
    // Public Constructors

    /**
     * Constructor for StringPair.
     * 
     * @param newName the new name.
     * @param newDescription the new description.
     */
    public StringPair(String newName, String newDescription) {
      name = newName;
      description = newDescription;
    }

    //--------------------------------------------------------------------------
    // Public Methods

    /**
     * Returns the name of the binding.
     * 
     * @return the binding's name.
     */
    public String getName() {
      return name;
    }

    /**
     * Returns the description of the binding.
     * 
     * @return the binding's description.
     */
    public String getDescription() {
      return description;
    }
  }
}
